package EP::EPC::Core::Data_Normalization;

use strict;

use EP::Common::Error qw ( :try );

use EP::Config;
use EP::Common;
use EP::Common::General;

use EP::EPC::Core;
use EP::EPC::Core::Component;

use File::Temp;
use File::Basename;

use PDL;
use PDL::IO::FlexRaw;

use EP::Rclient;

use base 'EP::EPC::Core::Component';

sub process_component
    {
    my $self = shift;

    my ( $new_dataset, $dataset_quality_graphics ) = $self->process_normalization( $self->{src_dataset} );

    my $process_result =
        { rows =>
            { type => "number"
            , label => "Rows after transform"
            , value => $new_dataset->data->matrix->dim(1)
            }
        , columns =>
            { type => "number"
            , label => "Columns after transform"
            , value => $new_dataset->data->matrix->dim(0)
            }
        , dataset_name => $new_dataset->{dataset}
        , src_dataset_id => $self->{src_dataset}->id
        , dst_dataset_id => $new_dataset->id
        , postnormpng =>
            { type => "image/png"
            , label => "Post-normalization plot"
            , value => $EP::Config::h->{EPNG_USRDATA_URL} . "/" . $self->{src_dataset}->folder->location . "/" . $self->{src_dataset}->filename . ".postnorm.png"
            }
        , heatmap => $dataset_quality_graphics->{heatmap}
        };

    $self->{CD}->{process_result} = $process_result;
    return $self->record_analysis_history();
    }

# sub: process_normalization
#   Entry point for normalization processing.
# returns:
#   The new resulting dataset.
sub process_normalization
    {
    my $self = shift;
    my ( $dataset ) = @_;

    my $query = $self->{query};

    $self->{session}->status("Processing Normalization ...");

    # check for missing values
    die "Cannot run normalization on data with missing values."
        if ( ( "affy" ne $dataset->type ) && any $dataset->data->matrix->isbad );

    die "Cannot run RMA/GCRMA on non-Affymetrix data."
        if ( ( $query->param ('normalization_gcrma') or $query->param('normalization_rma') ) and ( $dataset->type ne "affy" ) );

    $self->{session}->status("Running normalization...");

    my $rawdata = $EP::Config::h->{EPNG_USRDATA_PATH} . "/" . $dataset->folder->location . "/" . $dataset->filename . ".bin";
    $rawdata = $EP::Config::h->{EPNG_USRDATA_PATH} . "/" . $dataset->folder->location . "/" . $dataset->filename . ".Rdata"
        if ( $dataset->type eq "affy" );

    my $postnormpng = $EP::Config::h->{EPNG_USRDATA_PATH} . "/" . $dataset->folder->location . "/" . $dataset->filename . ".postnorm.png";
    my $tmp = new File::Temp;

    my $method;
    $method = "gcrma" if ( $query->param ('normalization_gcrma') );
    $method = "rma" if ( $query->param ('normalization_rma') );
    $method = "vsn" if ( $query->param ('normalization_vsn') );
    $method = "vsnaffy" if ( $query->param ('normalization_vsn') and ( $dataset->type eq "affy" ) );
    $method = "liwong" if ( $query->param ('normalization_liwong') and ( $dataset->type eq "affy" ) );

    die "Li & Wong normalization is implemented only for raw Affymetrix data (CEL-file uploads)."
        if ( $query->param ( 'normalization_liwong') and ( $dataset->type ne "affy" ) );

    EP::Rclient->R(<<__RCODE__);
app_root = "$EP::Config::h->{ROOT}";
source("$EP::Config::h->{ROOT}/cgi-lib/tmpl/R/ep.normalization.tmpl.R");
ep.normalization.$method( "$rawdata", "$postnormpng", "$tmp" );
__RCODE__

    my ( $r, $c, $new_exprs ) = readflex("$tmp");
    die "Normalization failed"
        if ( !( ref( $new_exprs ) =~ /PDL/ ) );

    # Copy the dataset now. Note: this causes $norm_dataset to point to the same raw data as the original.
    my $norm_dataset = $dataset->copy({ name => undef });
    $norm_dataset->type("exp");
    my $new_norm_data = $norm_dataset->data();

    $new_norm_data->{matrix} = $new_exprs;
    $new_norm_data->{row}    = $dataset->data->rows();
    $new_norm_data->{column} = $dataset->data->cols();

    EP::Core::Data::Parser::exp::user->_write_dataset_data( $norm_dataset, $norm_dataset->data );
    my $dataset_quality_graphics = $norm_dataset->data->process_metadata();

    # HACK. This is to force any future read of raw data on the original (pre-selection) dataset to re-read it from the
    # disk, rather than read the modified raw data from trf_dataset.
    delete $dataset->{data};

    return ( $norm_dataset, $dataset_quality_graphics );
    }

1;
